package com.cnaaa.pay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.cnaaa.pay.bo.TradeQuery;
import com.cnaaa.pay.dao.StringIdDao;
import com.cnaaa.pay.dao.TradeDao;
import com.cnaaa.pay.entity.Trade;
import com.cnaaa.pay.service.AlipayService;
import com.cnaaa.pay.service.TradeService;
import com.cnaaa.pay.util.IdentityUtil;
import com.cnaaa.pay.vo.CreateTradeVo;
import com.cnaaa.pay.vo.TradeStatusVo;
import com.cnaaa.pay.vo.alipay.AlipayNotifyVo;
import com.cnaaa.pay.vo.alipay.AlipayReturnVo;
import com.cnaaa.pay.vo.alipay.AlipayTradeStatus;
import com.cnaaa.pay.vo.alipay.TradeVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 2018/10/26 Create by 郭文梁
 * TradeServiceImpl
 * 订单业务实现
 *
 * @author 郭文梁
 * @date 2018/10/26
 */
@Service
@Slf4j
public class TradeServiceImpl extends AbstractStringIdServiceImpl<Trade> implements TradeService {
    /**
     * 超时分钟 10分钟
     */
    private static final long TIMEOUT_MINUTE = 10;
    private static final String TRADE_FINISHED = "TRADE_FINISHED";
    private static final String TRADE_SUCCESS = "TRADE_SUCCESS";
    private static final String TRADE_CLOSED = "TRADE_CLOSED";
    private static final String WAIT_BUYER_PAY = "WAIT_BUYER_PAY";
    private final TradeDao tradeDao;
    private final AlipayService alipayService;


    @Autowired
    public TradeServiceImpl(TradeDao tradeDao, AlipayService alipayService) {
        this.tradeDao = tradeDao;
        this.alipayService = alipayService;
    }

    @Override
    public StringIdDao<Trade> getRepository() {
        return tradeDao;
    }

    @Override
    public TradeVo create(CreateTradeVo createTradeVo) {
        String tradeNo = IdentityUtil.timeId();
        String id = IdentityUtil.randomUUID();
        if (checkTradeAndIdNotExists(id, tradeNo)) {
            Trade trade = new Trade();
            trade.setId(id);
            trade.setTradeNo(tradeNo);
            trade.setAmount(createTradeVo.getAmount());
            trade.setDescription(createTradeVo.getDescription());
            trade.setName(createTradeVo.getName());
            trade.setReturnUrl(createTradeVo.getReturnUrl());
            trade.setCreateTime(new Date());
            trade.setStatus(Trade.CREATE);
            trade.setProductId(createTradeVo.getProductId());
            trade.setProductName(createTradeVo.getProductName());
            trade.setUsername(createTradeVo.getUsername());
            trade.setUserId(createTradeVo.getUserId());
            Trade result = save(trade);
            if (result == null) {
                return null;
            }

            TradeVo tradeVo = new TradeVo();
            BeanUtils.copyProperties(result, tradeVo);
            return tradeVo;
        } else {
            return null;
        }
    }

    @Override
    public boolean checkTradeAndIdNotExists(String id, String tradeNo) {
        Trade trade = tradeDao.findByIdOrTradeNo(id, tradeNo);
        if (trade == null) {
            return true;
        }
        if (log.isDebugEnabled()) {
            log.debug("Find a trade have same id or tradeNo:{}, id={},tradeNo={}", trade, id, tradeNo);
        }
        return false;
    }

    @Override
    public Trade onPayReturn(AlipayReturnVo returnVo) {
        String outTradeNo = returnVo.getOutTradeNo();
        log.info("Trade {} return", returnVo.getOutTradeNo());
        Trade trade = tradeDao.findByTradeNo(outTradeNo);
        if (trade == null) {
            return null;
        }
        return trade.getStatus() == Trade.PAYED ? trade : null;
    }

    @Override
    public int closeTimeoutAlipayTrade() throws AlipayApiException {
        List<Trade> trades = tradeDao.timeoutTrades(TIMEOUT_MINUTE);
        int count = 0;
        for (Trade trade : trades) {
            trade.setStatus(Trade.CLOSED);
            trade.setCloseTime(new Date());
            trade = save(trade);
            boolean success = alipayService.close(trade);
            if (success) {
                count++;
            }
        }
        return count;
    }

    @Override
    public boolean onAlipayNotify(Map<String, String> params) {
        //第一步：检查签名
        boolean success = alipayService.checkReturnSign(params);
        if (!success) {
            return false;
        }
        AlipayNotifyVo notifyVo = JSON.parseObject(JSON.toJSONString(params), AlipayNotifyVo.class);

        //第二步：查看通知类型
        String tradeStatus = notifyVo.getTradeStatus();
        String tradeNo = notifyVo.getOutTrade();
        if (TRADE_FINISHED.equals(tradeStatus)) {
            log.info("Trade {} notify {}", tradeNo, "TRADE_FINISHED");
        } else if (TRADE_SUCCESS.equals(tradeStatus)) {
            log.info("Trade {} notify {}", tradeNo, "TRADE_SUCCESS");
            //支付成功通知：更新订单状态
            return uppdateTradeStatus2Payed(tradeNo);
        } else if (TRADE_CLOSED.equals(tradeStatus)) {
            log.info("Trade {} notify {}", tradeNo, "TRADE_CLOSED");
        } else if (WAIT_BUYER_PAY.equals(tradeStatus)) {
            log.info("Trade {} notify {}", tradeNo, "WAIT_BUYER_PAY");
        } else {
            return false;
        }
        return false;
    }

    @Override
    public Trade searchByTradeNo(String tradeNo) {
        return tradeDao.findByTradeNo(tradeNo);
    }

    @Override
    public TradeStatusVo status(String tradeId) {
        Trade trade = tradeDao.findById(tradeId).orElse(null);
        if (trade == null) {
            return null;
        }
        switch (trade.getPayBy()) {
            case Trade.ALIPAY: {
                return alipayTradeStatus(trade.getTradeNo());
            }
            case Trade.WECHAT: {
                return wechatTradeStatus(trade.getTradeNo());
            }
            default:
                return null;
        }
    }

    @Override
    public List<Trade> listByUser(long userId, int page, int size) {
        return tradeDao.listByUserId(userId, (page - 1) * size, size);
    }

    @Override
    public int getUserTradeCount(long userId) {
        return tradeDao.countAllByUserId(userId);
    }

    /**
     * 查询微信交易状态
     *
     * @param tradeNo 交易号
     * @return 交易状态
     */
    private TradeStatusVo wechatTradeStatus(String tradeNo) {
        //暂未接入微信支付
        return null;
    }

    /**
     * 查询支付宝交易状态
     *
     * @param tradeNo 交易号
     * @return 交易状态
     */
    private AlipayTradeStatus alipayTradeStatus(String tradeNo) {
        try {
            String result = alipayService.query(new TradeQuery(null, tradeNo));
            if (result == null) {
                return null;
            }
            JSONObject json = JSON.parseObject(result);
            return json.getObject("alipay_trade_query_response", AlipayTradeStatus.class);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 更新交易状态为已支付
     *
     * @param tradeNo 交易号
     * @return 是否设置成功
     */
    private boolean uppdateTradeStatus2Payed(String tradeNo) {
        //检查订单
        Trade trade = tradeDao.findByTradeNo(tradeNo);
        if (trade == null || trade.getStatus() != Trade.ACTIVE) {
            return false;
        }
        //更新订单状态
        trade.setStatus(Trade.PAYED);
        trade.setCompleteTime(new Date());
        trade = tradeDao.save(trade);
        return trade.getStatus() == Trade.PAYED;
    }
}
